﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: ByteArray.cs
* 文件说明: 基于内存可复用的字节数组对象
* 当前版本: V1.0
* 创建日期: 2024-06-01
*
* 2024-06-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Buffers;
using System.Diagnostics;

namespace Sunny.FrameDecoder;

/// <summary>
/// 接口
/// </summary>
public interface IByteArray
{
    /// <summary>
    /// 长度
    /// </summary>
    public int Length { get; }

    /// <summary>
    /// 转换为字节数组
    /// </summary>
    /// <returns></returns>
    public byte[] ToArray();
}

/// <summary>
/// 基于内存可复用的字节数组对象，用完必须释放
/// </summary>
[DebuggerDisplay("ByteArray: Length = {Length}")]
[DebuggerTypeProxy(typeof(ByteBufferDebugView))]
public class ByteArray : DisposableObject, IByteArray
{
    private byte[] _buffer;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="length">长度</param>
    /// <param name="clearArray">清空数组</param>
    public ByteArray(int length, bool clearArray = false)
    {
        if (length <= 0) throw new ArgumentOutOfRangeException("length");

        _buffer = ArrayPool<byte>.Shared.Rent(length);
        if (clearArray) _buffer.AsSpan().Clear();
        Length = length;
    }

    /// <summary>
    /// 释放对象
    /// </summary>
    /// <param name="disposing"></param>
    protected override void Dispose(bool disposing)
    {
        if (IsDisposed) return;

        if (disposing)
        {
            ArrayPool<byte>.Shared.Return(_buffer);
        }

        base.Dispose(disposing);
    }

    /// <summary>
    /// 字节
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public byte this[int index]
    {
        get
        {
            ThrowIfDisposed();
            return _buffer[index];
        }
        set
        {
            ThrowIfDisposed();
            _buffer[index] = value;
        }
    }

    /// <summary>
    /// 长度
    /// </summary>
    public int Length { get; private set; }

    /// <summary>
    /// Span对象
    /// </summary>
    public Span<byte> Span
    {
        get
        {
            ThrowIfDisposed();
            return _buffer.AsSpan().Slice(0, Length);
        }
    }
    /// <summary>
    /// 转换为字节数组
    /// </summary>
    /// <returns></returns>
    public byte[] ToArray()
    {
        ThrowIfDisposed();
        return Span.ToArray();
    }

    /// <summary>
    /// 重置大小
    /// </summary>
    /// <param name="newSize"></param>
    public ByteArray Resize(int newSize)
    {
        if (newSize <= 0) throw new ArgumentOutOfRangeException("newSize");
        ThrowIfDisposed();

        if (newSize > _buffer.Length)
        {
            var buffer = ArrayPool<byte>.Shared.Rent(newSize);
            _buffer.AsSpan().CopyTo(buffer.AsSpan());
            ArrayPool<byte>.Shared.Return(_buffer);
            _buffer = buffer;
        }

        Length = newSize;
        return this;
    }

    /// <summary>
    /// 切片
    /// </summary>
    /// <param name="start">起始位置</param>
    /// <param name="length">长度</param>
    /// <returns>数据对象</returns>
    public Span<byte> Slice(int start, int length)
    {
        if (start > Length || start + length > Length) ThrowHelper.ThrowArgumentOutOfRangeException();
        ThrowIfDisposed();
        return Span.Slice(start, length);
    }

    /// <summary>
    /// 切片
    /// </summary>
    /// <param name="start">起始位置</param>
    /// <returns>数据对象</returns>
    public Span<byte> Slice(int start)
    {
        if (start > Length) ThrowHelper.ThrowArgumentOutOfRangeException();
        ThrowIfDisposed();
        return Span.Slice(start);
    }

    /// <summary>
    /// 清空
    /// </summary>
    public void Clear()
    {
        ThrowIfDisposed();
        _buffer.AsSpan().Clear();
    }

    /// <summary>
    /// 复制
    /// </summary>
    /// <param name="destination">目标</param>
    public void CopyTo(Span<byte> destination)
    {
        ThrowIfDisposed();
        Span.CopyTo(destination);
    }

    /// <summary>
    /// 尝试复制
    /// </summary>
    /// <param name="destination">目标</param>
    /// <returns>结果</returns>
    public bool TryCopyTo(Span<byte> destination)
    {
        ThrowIfDisposed();
        return Span.TryCopyTo(destination);
    }

    /// <summary>
    /// 填充
    /// </summary>
    /// <param name="value">值</param>
    public void Fill(byte value)
    {
        ThrowIfDisposed();
        _buffer.AsSpan().Fill(value);
    }

    /// <summary>
    /// 定义ReadOnlySpan的隐式转换
    /// </summary>
    /// <param name="ba"></param>
    public static implicit operator ReadOnlySpan<byte>(ByteArray ba) => ba.Span;

    /// <summary>
    /// 定义Span的隐式转换
    /// </summary>
    /// <param name="ba"></param>
    public static implicit operator Span<byte>(ByteArray ba) => ba.Span;

    /// <summary>
    /// 定义byte[]的隐式转换
    /// </summary>
    /// <param name="ba"></param>
    public static implicit operator byte[](ByteArray ba) => ba.ToArray();

    /// <summary>
    /// 转换为字符串
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return $"ByteArray: Length = {Length}";
    }

    /// <summary>
    /// 获取基础字节数组对象
    /// </summary>
    /// <returns>字节数组</returns>
    public byte[] GetBaseStream()
    {
        ThrowIfDisposed();
        return _buffer;
    }

    /// <summary>
    /// 从数据源复制数据
    /// </summary>
    /// <param name="span">数据源</param>
    /// <returns>字节数组对象</returns>
    public ByteArray PasteFrom(ReadOnlySpan<byte> span)
    {
        if (span == null || span.Length == 0) throw new ArgumentNullException(nameof(span));

        Resize(span.Length);
        span.CopyTo(Span);
        return this;
    }

    /// <summary>
    /// 从数据源复制数据
    /// </summary>
    /// <param name="bytes">数据源</param>
    /// <param name="start">起始位置</param>
    /// <param name="length">长度</param>
    /// <returns>字节数组对象</returns>
    public ByteArray PasteFrom(byte[] bytes, int start, int length)
    {
        if (start > bytes.Length || start + length > bytes.Length)
            ThrowHelper.ThrowArgumentOutOfRangeException();

        Resize(length);
        bytes.AsSpan().Slice(start, length).CopyTo(Span);
        return this;
    }
}

/// <summary>
/// 调试视图
/// </summary>
public class ByteBufferDebugView
{
    /// <summary>
    /// 对象
    /// </summary>
    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
    public byte[] Items { get; }

    /// <summary>
    /// 调试视图
    /// </summary>
    /// <param name="owner"></param>
    public ByteBufferDebugView(IByteArray owner)
    {
        this.Items = owner.ToArray();
    }
}
